﻿using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using O1.Kernel;
using O1.Kernel.Primitives;

namespace Tests.O1.Kernel.Primitives
{
    [TestClass]
    public class HashPropertyTests
    {
        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyStringInitialzerHashesUsingConsistentApproach()
        {
            var value = "This is a 'long' (ish) string value that will be hashed.";
            var hash = value.ToHashGuid();

            var property1 = new HashProperty(55, value);
            var property2 = new HashProperty(55, hash);

            property1.Equals(property2).Should().BeTrue();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyWritesDataIntoPackedSizeBufferAsExpected()
        {
            var subject = new HashProperty(6, Guid.NewGuid());
            var buffer = new byte[HashProperty.Size];
            subject.Write(buffer, 0);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyWritesDataValuesAsExpected()
        {
            var value = Guid.NewGuid();
            var subject = new HashProperty(99, value);
            var buffer = new byte[HashProperty.Size];
            subject.Write(buffer, 0);

            var actualValue = buffer.ToGuid(0);
            var actualKey = BitConverter.ToInt32(buffer, 16);

            actualValue.Should().Be(value);
            actualKey.Should().Be(99);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyReadsAsExpected()
        {
            var value = Guid.NewGuid();
            var buffer = new byte[HashProperty.Size];
            value.CopyBytes(buffer, 0);
            var key = BitConverter.GetBytes(77);
            Array.Copy(key, 0, buffer, Sz.CLR.Decimal, 4);

            var subject = HashProperty.Read(buffer, 0);
            subject.Key.Should().Be(77);
            subject.Value.Should().Be(value);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyWritesThenReadsAsExpected()
        {
            var expected = new HashProperty(99, Guid.NewGuid());
            var buffer = new byte[HashProperty.Size];
            expected.Write(buffer, 0);

            var actual = HashProperty.Read(buffer, 0);
            actual.Key.Should().Be(expected.Key);
            actual.Value.Should().Be(expected.Value);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyEqualityIsKeyValueEquality()
        {
            var value = Guid.NewGuid();
            var subject1 = new HashProperty(99, value);
            var subject2 = new HashProperty(99, value);
            (subject1 == subject2).Should().BeTrue();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyInequalityIsKeyInequality()
        {
            var value = Guid.NewGuid();
            var subject1 = new HashProperty(99, value);
            var subject2 = new HashProperty(98, value);
            (subject1 != subject2).Should().BeTrue();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyInequalityIsValueInequality()
        {
            var subject1 = new HashProperty(98, Guid.NewGuid());
            var subject2 = new HashProperty(98, Guid.NewGuid());
            (subject1 != subject2).Should().BeTrue();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyEqualsIsValueEquality()
        {
            var value = Guid.NewGuid();
            var subject1 = new HashProperty(99, value);
            var subject2 = new HashProperty(99, value);
            subject1.Equals(subject2).Should().BeTrue();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyEqualsIsKeyInequality()
        {
            var value = Guid.NewGuid();
            var subject1 = new HashProperty(99, value);
            var subject2 = new HashProperty(98, value);
            subject1.Equals(subject2).Should().BeFalse();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyEqualsIsValueInequality()
        {
            var subject1 = new HashProperty(98, Guid.NewGuid());
            var subject2 = new HashProperty(98, Guid.NewGuid());
            subject1.Equals(subject2).Should().BeFalse();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyIsNotEqualToNull()
        {
            object other = null;
            new HashProperty(98, Guid.NewGuid()).Equals(other).Should().BeFalse();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyGetHashCodeIsConsistent()
        {
            var subject = new HashProperty(98, Guid.NewGuid());
            subject.GetHashCode().Should().Be(subject.GetHashCode());   
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyGetHashCodeIsDifferentForDifferentKey()
        {
            var value = Guid.NewGuid();
            var subject1 = new HashProperty(98, value);
            var subject2 = new HashProperty(101, value);
            subject1.GetHashCode().Should().NotBe(subject2.GetHashCode());
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyGetHashCodeIsDifferentForDifferentValue()
        {
            var subject1 = new HashProperty(101, Guid.NewGuid());
            var subject2 = new HashProperty(101, Guid.NewGuid());
            subject1.GetHashCode().Should().NotBe(subject2.GetHashCode());
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyGetHashCodeIsDifferentForDifferentKeyAndValue()
        {
            var subject1 = new HashProperty(88, Guid.NewGuid());
            var subject2 = new HashProperty(101, Guid.NewGuid());
            subject1.GetHashCode().Should().NotBe(subject2.GetHashCode());
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyObjectEqualsIsEquality()
        {
            var value = Guid.NewGuid();
            var subject1 = new HashProperty(98, value);
            object subject2 = new HashProperty(98, value);
            subject1.Equals(subject2).Should().BeTrue();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyObjectEqualsIsInequalityForDifferentKey()
        {
            var value = Guid.NewGuid();
            var subject1 = new HashProperty(99, value);
            object subject2 = new HashProperty(98, value);
            subject1.Equals(subject2).Should().BeFalse();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyObjectEqualsIsInequalityForDifferentValue()
        {
            var subject1 = new HashProperty(99, Guid.NewGuid());
            object subject2 = new HashProperty(99, Guid.NewGuid());
            subject1.Equals(subject2).Should().BeFalse();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyObjectEqualsIsInequalityForDifferentKeyAndValue()
        {
            var subject1 = new HashProperty(8769, Guid.NewGuid());
            object subject2 = new HashProperty(99, Guid.NewGuid());
            subject1.Equals(subject2).Should().BeFalse();
        }

        [TestMethod, TestCategory("Unit Test")]
        public void HashPropertyObjectEqualsIsInequalityForDifferentType()
        {
            var subject1 = new HashProperty(99, Guid.NewGuid());
            object subject2 = new object();
            subject1.Equals(subject2).Should().BeFalse();
        }
    }
}
